﻿using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Security;
using Agility.Web.Models.RegistrationForm;
using Agility.Web.Services;
using NUnit.Framework;
using Moq;
using Microsoft.ServiceModel.DomainServices.Server.UnitTesting;

namespace Agility.Testing.Web.UserManagement
{
    [TestFixture]
    public class UserRegistrationServiceTests
    {
        OneOfAKind.Toolbox.NetFxTools.PrivateObject privateObject;
        Mock<IRolesWrapper> rw;
        Mock<IProfileWrapper> pw;
        Mock<IMembershipWrapper> mw;

        #region optional setup and tear down methods
        //// Called before the first test to setup the whole fixture
        [TestFixtureSetUp]
        public void TestFixtureSetup()
        {
            privateObject = new OneOfAKind.Toolbox.NetFxTools.PrivateObject("Agility.Web.dll", "Agility.Web.Services.UserRegistrationService");
            
        }

        //// Called after the last test to tear down the whole fixture
        //[TestFixtureTearDown]
        //public void TestFixtureTearDown()
        //{
        //}

        //// Called before each test
        //[SetUp]
        //public void TestSetup()
        //{
        //}

        //// Called after each test
        //[TearDown]
        //public void TestTearDown()
        //{ 
        //}

        #endregion

        #region UnitTests
        [Test]
        [Category("UnitTest")]
        [TestCase(MembershipCreateStatus.DuplicateEmail, Result = CreateUserStatus.DuplicateEmail, TestName="DuplicateEmail")]
        [TestCase(MembershipCreateStatus.DuplicateUserName, Result = CreateUserStatus.DuplicateUserName, TestName = "DuplicateUserName")]
        [TestCase(MembershipCreateStatus.InvalidAnswer, Result = CreateUserStatus.InvalidAnswer, TestName = "InvalidAnswer")]
        [TestCase(MembershipCreateStatus.InvalidEmail, Result = CreateUserStatus.InvalidEmail, TestName = "InvalidEmail")]
        [TestCase(MembershipCreateStatus.InvalidPassword, Result = CreateUserStatus.InvalidPassword, TestName = "InvalidPassword")]
        [TestCase(MembershipCreateStatus.InvalidQuestion, Result = CreateUserStatus.InvalidQuestion, TestName = "InvalidQuestion")]
        [TestCase(MembershipCreateStatus.InvalidUserName, Result = CreateUserStatus.InvalidUserName, TestName = "InvalidUserName")]
        [TestCase(MembershipCreateStatus.Success, Result = CreateUserStatus.Success, TestName = "Success")]
        [TestCase(MembershipCreateStatus.DuplicateProviderUserKey, Result = CreateUserStatus.Failure, TestName = "DuplicateProviderUserKey")]
        [TestCase(MembershipCreateStatus.InvalidProviderUserKey, Result = CreateUserStatus.Failure, TestName = "InvalidProviderUserKey")]
        [TestCase(MembershipCreateStatus.ProviderError, Result = CreateUserStatus.Failure, TestName = "ProviderError")]
        [TestCase(MembershipCreateStatus.UserRejected, Result = CreateUserStatus.Failure, TestName = "UserRejected")]
        public CreateUserStatus TestConvertStatus(MembershipCreateStatus status)
        {
            object[] args = {status};
            return (CreateUserStatus)this.privateObject.InvokeStatic("ConvertStatus", args);
        }

        // MetaData Tests
        [Test]
        [Category("UnitTest")]
        public void TestMetadataCreateUserUser()
        {
            var methodInfo = typeof(UserRegistrationService).GetMethod("CreateUser");
            var parameterInfo = methodInfo.GetParameters().SingleOrDefault(p => p.Name == "user");
            Assert.IsNotNull(parameterInfo);
            Assert.AreEqual(0, parameterInfo.GetCustomAttributes(false).Count(), "Unexpected attributes are on CreateUser parameter user");
        }

        [Test]
        [Category("UnitTest")]
        public void TestMetadataCreateUserPasswordRequired()
        {
            var methodInfo = typeof(UserRegistrationService).GetMethod("CreateUser");
            var parameterInfo = methodInfo.GetParameters().SingleOrDefault(p => p.Name == "password");
            Assert.IsNotNull(parameterInfo);
            var attribute = parameterInfo.GetCustomAttributes(typeof(RequiredAttribute), false)
                                         .Cast<RequiredAttribute>()
                                         .FirstOrDefault();
            Assert.NotNull(attribute, "RequiredAttribute is missing on CreateUser parameter password");
        }

        [Test]
        [Category("UnitTest")]
        public void TestMetadataCreateUserPasswordStringLength()
        {
            var methodInfo = typeof(UserRegistrationService).GetMethod("CreateUser");
            var parameterInfo = methodInfo.GetParameters().SingleOrDefault(p => p.Name == "password");
            Assert.IsNotNull(parameterInfo);
            var attribute = parameterInfo.GetCustomAttributes(typeof(StringLengthAttribute), false)
                                         .Cast<StringLengthAttribute>()
                                         .FirstOrDefault();
            Assert.NotNull(attribute, "StringLengthAttribute is missing on CreateUser parameter password");
            Assert.AreEqual(8, attribute.MinimumLength);
            Assert.AreEqual(50, attribute.MaximumLength);
        }

        [Test]
        [Category("UnitTest")]
        public void TestMetadataCreateUserPasswordRegularExpression()
        {
            var methodInfo = typeof(UserRegistrationService).GetMethod("CreateUser");
            var parameterInfo = methodInfo.GetParameters().SingleOrDefault(p => p.Name == "password");
            Assert.IsNotNull(parameterInfo);
            var attribute = parameterInfo.GetCustomAttributes(typeof(RegularExpressionAttribute), false)
                                         .Cast<RegularExpressionAttribute>()
                                         .FirstOrDefault();
            Assert.NotNull(attribute, "RegularExpressionAttribute is missing on CreateUser parameter password");
            Assert.AreEqual(@"^(?=.*[a-z])(?=.*[A-Z])(?=.*[\d])(?=.*[\W]).*$", attribute.Pattern);
        }

        #endregion

        #region ComponentTests

        public UserRegistrationService CreateDomainService()
        {
            return new UserRegistrationService(mw.Object, rw.Object, pw.Object);
        }

        [Test]
        [Category("ComponentTest")]
        public void TestCreateUser()
        {
            //Arrange
            rw = new Mock<IRolesWrapper>();
            rw.Setup<bool>(r => r.RoleExists(It.IsAny<string>())).Returns(true);
            rw.Setup(r => r.CreateRole(It.IsAny<string>()));
            rw.Setup(r => r.AddUserToRole(It.IsAny<string>(), It.IsAny<string>()));

            mw = new Mock<IMembershipWrapper>();
            MembershipCreateStatus status = MembershipCreateStatus.Success;
            mw.Setup<MembershipUser>(m => m.CreateUser(
                It.Is<string>(u => u.Length > 4),
                It.IsAny<string>(),
                It.IsAny<string>(),
                It.IsAny<string>(),
                It.IsAny<string>(),
                It.IsAny<bool>(),
                It.IsAny<object>(),
                out status));

            pw = new Mock<IProfileWrapper>();
            pw.Setup(p => p.Create(It.IsAny<string>(), It.IsAny<bool>())).Returns(pw.Object);
            pw.Setup(p => p.SetPropertyValue(It.IsAny<string>(), It.IsAny<string>()));
            pw.Setup(p => p.Save());
                        
            RegistrationData user = new RegistrationData();
            user.Answer = "The Answer";
            user.Email = "testuser@testing.com";
            user.FriendlyName = "FriendlyName";
            user.Question = "What?";
            user.UserName = "TestUser";

            string password = "Test##100";

            // Act
            DomainServiceTestHost<UserRegistrationService> target = new DomainServiceTestHost<UserRegistrationService>(this.CreateDomainService);
            target.Invoke(ds => ds.CreateUser(user, password));

            //Assert
            mw.VerifyAll();
            rw.Verify(r => r.RoleExists(It.IsAny<string>()));
            rw.Verify(r => r.CreateRole(It.IsAny<string>()), Times.Never());
            rw.Verify(r => r.AddUserToRole(user.UserName, It.IsAny<string>()));
            pw.Verify(p => p.Create(user.UserName, true));
            pw.Verify(p => p.SetPropertyValue("FriendlyName", user.FriendlyName));
            pw.Verify(p => p.Save());
        }
        #endregion
    }

}
